/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

package org.rainbow.xml;

import org.rainbow.annotation.AnnotationUtils;
import org.rainbow.udo.MultiResultsRuntimeException;
import java.io.IOException;
import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import org.w3c.dom.Document;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;

/**
 *
 * @author hironaga
 */
class XmlHandleHelperImpl_v01 extends XmlHandleHelper {

    private XmlCommonHandler commonHandler = XmlCommonHandler.getInstance();

    XmlHandleHelperImpl_v01() {}

    <T> T getElementData(Document doc, String xpath, Class<T> cls)
               throws IOException, InstantiationException, IllegalAccessException {

        NodeList nodeList = commonHandler.getNodeList(doc, xpath);
        if(nodeList == null || nodeList.getLength() == 0){
            return null;
        }

        //Get Fields
        Field[] fields = cls.getDeclaredFields();
        //If No Field exists
        if(fields == null || fields.length == 0){
            return null;
        }

        if(nodeList.getLength() > 1 ){
            throw new MultiResultsRuntimeException("NodeList is not Single");
        }

        T obj = getElementData(doc, nodeList.item(0), xpath, cls, fields);

        return obj;
    }

    public <T> List<T> getElementDataList(Document doc, String xpath, Class<T> cls)
               throws IOException, InstantiationException, IllegalAccessException {

        NodeList nodeList = commonHandler.getNodeList(doc, xpath);
        if(nodeList == null || nodeList.getLength() == 0){
            return null;
        }

        //Get Fields
        Field[] fields = cls.getDeclaredFields();
        //If No Field exists
        if(fields == null || fields.length == 0){
            return null;
        }

        //New Instance(Type:List)   TODO リスト型も外部から注入する必要あり
        List<T> objList = new ArrayList<T>();

        int nodeLength = nodeList.getLength();
        loop0: //loop:Node-List
        for(int idx0=0; idx0<nodeLength; idx0++){

            //TODO キャストしないやり方に変える
            T obj = getElementData(doc, nodeList.item(idx0), xpath, cls, fields);
            //Add Object
            objList.add(obj);

        }//loop0

        return objList;
    }

    protected <T> T getElementData(Document doc, Node parentElement, String xpath,
                                                  Class<T> cls, Field[] fields)
             throws IOException, InstantiationException, IllegalAccessException{

        //New Instance(Type:Object<T>)
        T obj = cls.newInstance();

        loop1:  //loop:Field
        for(int idx1=0; idx1<fields.length; idx1++){

            //Get Field Annotation
            Annotation[] annotations = fields[idx1].getDeclaredAnnotations();
            //If No Annotations Exist
            if(annotations == null || annotations.length == 0){
                continue loop1;
            }
            //If Annotations Exist
            else{
                //Field
                String fieldName = fields[idx1].getName();

                //Method "setter"
                String methodName = AnnotationUtils.getSetterMethodname(fieldName);
                Method setter = null;
                try{
                    setter = cls.getMethod(methodName, fields[idx1].getType());
                    //TODO DATE型対応
                    if(fields[idx1].getType() == Date.class){
                        setter = cls.getMethod(methodName.concat("String"), String.class);
                    }
                } catch(NoSuchMethodException ex){
                    continue loop1;
                }
                finally{
                    if(setter == null){
                        continue loop1;
                    }
                }

                loop2:  //loop Field-Annotations
                for(int idx2=0; idx2<annotations.length; idx2++){

                    Annotation annotation = annotations[idx2];

                    XmlHandleComponent component =
                            XmlHandleCommand.getHelperComponent(annotation.annotationType());

                    if(component == null){
                        continue loop2;
                    }

                    //Read XML
                    component.readElement(annotation, setter, fields[idx1],
                                        obj, doc, xpath, parentElement, this);

                }//loop2
            }//if
        }//loop1

        return obj;

    }

    public void createElement(Object obj, Document doc, String xpath,
                                                 Node parentElement, int index )
                                                            throws IOException {
        //NULL
        if(obj == null){
            return;
        }

        //Set Class
        Class<?> cls = obj.getClass();

        //Get Fields
        Field[] fields = cls.getDeclaredFields();

        //Read Annotation
        loop1:  //loop:Field
        for(int idx1=0; idx1<fields.length; idx1++){

            //Get Field Annotation
            Annotation[] annotations = fields[idx1].getDeclaredAnnotations();
            //If No Annotations Exist
            if(annotations == null || annotations.length == 0){
                continue loop1;
            }
            //If Annotations Exist
            else{
                //Field
                String fieldName = fields[idx1].getName();

                //Method "getter"
                String methodName = AnnotationUtils.getGetterMethodname(fieldName);
                //TODO DATE型対応
                if(fields[idx1].getType() == Date.class){
                    methodName = methodName.concat("String");
                }
                Method getter = null;
                try{
                    Class[] types = new Class[]{};
                    getter = cls.getMethod( methodName, types );
                } catch(NoSuchMethodException ex){
                    continue loop1;
                }
                finally{
                    if(getter == null){
                        continue loop1;
                    }
                }

                loop2:  //loop Field-Annotations
                for(int idx2=0; idx2<annotations.length; idx2++){

                    Annotation annotation = annotations[idx2];

                    XmlHandleComponent component =
                            XmlHandleCommand.getHelperComponent(annotation.annotationType());

                    if(component == null){
                        continue loop2;
                    }

                    //Write XML
                    component.writeElement(annotation, getter, fields[idx1]
                            , obj, doc, xpath, index, parentElement, this);

                }//loop2
            }//if
        }//loop1

    }

    public void overwriteElement(Object obj, Document doc, String xpath, Node parentElement, int index)
                                                            throws IOException {

        //Set Class
        Class<?> cls = obj.getClass();

        //Get Fields
        Field[] fields = cls.getDeclaredFields();

        //Read Annotation
        loop1:  //loop:Field
        for(int idx1=0; idx1<fields.length; idx1++){

            //Get Field Annotation
            Annotation[] annotations = fields[idx1].getDeclaredAnnotations();
            //If No Annotations Exist
            if(annotations == null || annotations.length == 0){
                continue loop1;
            }
            //If Annotations Exist
            else{
                //Field
                String fieldName = fields[idx1].getName();

                //Method "getter"
                String methodName = AnnotationUtils.getGetterMethodname(fieldName);
                //TODO DATE型対応
                if(fields[idx1].getType() == Date.class){
                    methodName = methodName.concat("String");
                }
                Method getter = null;
                try{
                    Class[] types = new Class[]{};
                    getter = cls.getMethod( methodName, types );
                } catch(NoSuchMethodException ex){
                    continue loop1;
                }
                finally{
                    if(getter == null){
                        continue loop1;
                    }
                }

                loop2:  //loop Field-Annotations
                for(int idx2=0; idx2<annotations.length; idx2++){

                    Annotation annotation = annotations[idx2];

                    XmlHandleComponent component =
                            XmlHandleCommand.getHelperComponent(annotation.annotationType());

                    if(component == null){
                        continue loop2;
                    }

                    //OverWrite XML
                    component.overwriteElement(annotation, getter, fields[idx1]
                            , obj, doc, xpath, index, parentElement, this);

                }//loop2
            }//if
        }//loop1

    }
    
}
